package ledgerapi

import (
	"encoding/json"

	"github.com/hyperledger/fabric-chaincode-go/shim"
	"github.com/hyperledger/fabric-protos-go/ledger/queryresult"
)

const (
	REL = "REL-"
)

type RelatedKeyStub struct {
	stub shim.ChaincodeStubInterface
}

func (ks *RelatedKeyStub) PutState(key string, value []byte, relatedKeys ...string) error {
	err := ks.stub.PutState(key, value)
	if err != nil {
		return err
	}

	if len(relatedKeys) == 0 {
		return nil
	}

	relatedKs, err := json.Marshal(relatedKeys)
	if err != nil {
		return err
	}

	err = ks.stub.PutState(REL + key, relatedKs)
	if err != nil {
		return err
	}

	for i := 0; i < len(relatedKeys); i++ {
		err := ks.stub.PutState(relatedKeys[i], []byte(key))
		if err != nil {
			return err
		}
	}
	return nil
}

func (ks *RelatedKeyStub) DelState(key string) error {

	keyRel := REL + key
	relatedKs, err := ks.stub.GetState(keyRel)
	if err != nil {
		return err
	}

	err = ks.stub.DelState(key)
	if err != nil {
		return err
	}

	if relatedKs == nil {
		return nil
	}

	var relatedKeys = []string{}
	err = json.Unmarshal(relatedKs, &relatedKeys)
	if err != nil {
		return err
	}

	for i := 0; i < len(relatedKeys); i++ {
		err = ks.stub.DelState(relatedKeys[i])
		if err != nil {
			return err
		}
	}
	err = ks.stub.DelState(keyRel)
	if err != nil {
		return err
	}
	
	return nil
}

func (ks *RelatedKeyStub) GetStateByPartialRelatedCompositeKey(objectType string, keys []string) (shim.StateQueryIteratorInterface, error) {
	ksIte, err := ks.stub.GetStateByPartialCompositeKey(objectType, keys)
	if err != nil {
		return nil, err
	}
	return &relatedKeyStateQueryIterator{
		stub:  ks.stub,
		ksIte: ksIte,
	}, nil
}

type relatedKeyStateQueryIterator struct {
	stub shim.ChaincodeStubInterface
	ksIte shim.StateQueryIteratorInterface
}

func (rki *relatedKeyStateQueryIterator) HasNext() bool {
	return rki.ksIte.HasNext()
}

func (rki *relatedKeyStateQueryIterator) Close() error{
	return rki.ksIte.Close()
}

func (rki *relatedKeyStateQueryIterator) Next() (*queryresult.KV, error){
	kv, err := rki.ksIte.Next()
	if err != nil {
		return nil, err
	}

	key := string(kv.Value)
	v, err := rki.stub.GetState(key)
	if err != nil {
		return nil, err
	}

	rkv := &queryresult.KV{
		Namespace:            kv.Namespace,
		Key:                  key,
		Value:                v,
		XXX_NoUnkeyedLiteral: kv.XXX_NoUnkeyedLiteral,
		XXX_unrecognized:     kv.XXX_unrecognized,
		XXX_sizecache:        kv.XXX_sizecache,
	}
	return rkv, nil

}




